package com.dayuanit.dy16.atm.service.impl;

import com.dayuanit.dy16.atm.domain.FlowDomain;
import com.dayuanit.dy16.atm.dto.FlowDTO;
import com.dayuanit.dy16.atm.entity.Card;
import com.dayuanit.dy16.atm.entity.Flow;
import com.dayuanit.dy16.atm.exception.BizException;
import com.dayuanit.dy16.atm.holder.PageHolder;
import com.dayuanit.dy16.atm.mapper.CardMapper;
import com.dayuanit.dy16.atm.mapper.FlowMapper;
import com.dayuanit.dy16.atm.service.IFlowService;
import com.dayuanit.dy16.atm.service.IRedisService;
import com.dayuanit.dy16.atm.util.CardUtils;
import com.dayuanit.dy16.atm.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class FlowServiceImpl implements IFlowService {

    @Autowired
    private FlowMapper flowMapper;

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    @Qualifier(value = "redisService4SpringImpl")
    private IRedisService redisService;

    @Override
    public PageHolder<FlowDTO> queryFlows(long cardId, String password, int currentPage) {
        Card card = cardMapper.selectByPrimaryKey(cardId);
        if (null == card
            || !card.getPwd().equals(password)) {
            throw new BizException("卡号不存在或者密码错误");
        }

        int total = flowMapper.countFlows(cardId);

        PageHolder<FlowDTO> pageHolder = PageHolder.build(total, currentPage);

        List<Flow> flows = flowMapper.listFlows(cardId, pageHolder.offset(), pageHolder.getPageSize());

        //防止数组扩容
        List<FlowDTO> dtos = new ArrayList(flows.size());
//        List<FlowDTO> dtos = new ArrayList();

        for (Flow flow : flows) {
            FlowDTO flowDTO = new FlowDTO();
            flowDTO.setAmount(flow.getAmount());
            flowDTO.setCardNumber(CardUtils.markCardNum(card.getCardNum()));
            flowDTO.setCreateTime(DateUtils.date2String(flow.getCreateTime()));
            flowDTO.setRemark(flow.getRemark());

            dtos.add(flowDTO);
        }

        pageHolder.setData(dtos);

        return pageHolder;
    }

    /**
     * 查询了N+1次  11
     * @param userId
     * @return
     */
    @Override
    public List<FlowDTO> queryTopFlow(long userId) {
        List<Flow> flows = flowMapper.listTop10(userId);
        List<FlowDTO> dtos = new ArrayList(flows.size());

        for (Flow flow : flows) {
            Card card = cardMapper.selectByPrimaryKey(flow.getCardId());

            FlowDTO flowDTO = new FlowDTO();
            flowDTO.setAmount(flow.getAmount());
            flowDTO.setCardNumber(CardUtils.markCardNum(card.getCardNum()));
            flowDTO.setCreateTime(DateUtils.date2String(flow.getCreateTime()));
            flowDTO.setRemark(flow.getRemark());

            dtos.add(flowDTO);
        }
        return dtos;
    }

    /**
     * 查询了两次
     * @param userId
     * @return
     */
    @Override
    public List<FlowDTO> queryTopFlow2(long userId) {
        List<Flow> flows = flowMapper.listTop10(userId);
        List<FlowDTO> dtos = new ArrayList(flows.size());

        Set<Long> cardIds = new HashSet(flows.size());
        for (Flow flow : flows) {
            cardIds.add(flow.getCardId());
        }

        List<Card> cards = cardMapper.listByIds(cardIds);

        //方案一：时间复杂度：n^2
//        for (Flow flow : flows) {
//            FlowDTO flowDTO = new FlowDTO();
//            flowDTO.setAmount(flow.getAmount());
//            for (Card card : cards) {
//                if (flow.getCardId().equals(card.getId())) {
//                    flowDTO.setCardNumber(CardUtils.markCardNum(card.getCardNum()));
//                }
//            }
//
//            flowDTO.setCreateTime(DateUtils.date2String(flow.getCreateTime()));
//            flowDTO.setRemark(flow.getRemark());
//
//            dtos.add(flowDTO);
//        }

//        方案二： k=卡ID v=卡号
        Map<Long , String> cardMap = new HashMap();
        for (Card card : cards) {
            cardMap.put(card.getId(), card.getCardNum());
        }

        //时间复杂度：n
        for (Flow flow : flows) {
            FlowDTO flowDTO = new FlowDTO();
            flowDTO.setAmount(flow.getAmount());
            flowDTO.setCardNumber(CardUtils.markCardNum(cardMap.get(flow.getCardId())));
            flowDTO.setCreateTime(DateUtils.date2String(flow.getCreateTime()));
            flowDTO.setRemark(flow.getRemark());

            dtos.add(flowDTO);
        }

        return dtos;
    }


    /**
     * 方案三：一条联合查询sql搞定
     * @param userId
     * @return
     */
    @Override
    public List<FlowDTO> queryTopFlow3(long userId) {
        //TODO 先到redis中查看，若有，则直接返回；反之，查询DB，再将数据放到Redis中。

        List<FlowDTO> flowDTOS = redisService.queryTop10Flow(userId);
        if (!flowDTOS.isEmpty()) {
            System.out.println(">>>>>top10 flow read from redis");
            return flowDTOS;
        }

        List<FlowDomain> flowDomains = flowMapper.listTop10Second(userId);
        List<FlowDTO> dtos = new ArrayList(flowDomains.size());
        for (FlowDomain flowDomain : flowDomains) {
            FlowDTO flowDTO = new FlowDTO();
            flowDTO.setAmount(flowDomain.getAmount());
            flowDTO.setCardNumber(CardUtils.markCardNum(flowDomain.getCardNum()));
            flowDTO.setCreateTime(DateUtils.date2String(flowDomain.getCreateTime()));
            flowDTO.setRemark(flowDomain.getRemark());

            dtos.add(flowDTO);
        }

        //放到redis中
        redisService.saveTop10Flow(userId, dtos);
        return dtos;
    }

    @Override
    public List<FlowDTO> downLoadFlow(long cardId, String password, long userId) {

        Card card = cardMapper.selectByPrimaryKey(cardId);
        if (null == card || !card.getPwd().equals(password)) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        if (userId != card.getUserId()) {
            throw new BizException("你无权操作");
        }

        List<FlowDomain> flows = flowMapper.listFlows4DownLoad(cardId);
        List<FlowDTO> dtos = new ArrayList(flows.size());

        for (FlowDomain flow : flows) {
            FlowDTO flowDTO = new FlowDTO();
            dtos.add(flowDTO);

            flowDTO.setAmount(flow.getAmount());
            flowDTO.setCardNumber(flow.getCardNum());
            flowDTO.setCreateTime(DateUtils.date2String(flow.getCreateTime()));
            flowDTO.setRemark(flow.getRemark());
        }

        return dtos;
    }
}
